//
// Created by QU on 24-6-27.
//

#include "json_parser.h"

using aquawius::json::logger::Logger;
using aquawius::json::logger::LOG_LEVELS;

namespace aquawius::json
{
    JsonObject JsonObject::getValue(const std::string& strKey) const {
        // 一个大函数, 中间使用constexpr if + std::is_same_v() 判定是什么数据类型.
        auto dovisit = [&](const auto& innerValue) -> JsonObject
        {
            using ValueType = std::decay_t<decltype(innerValue)>;
            if constexpr (std::is_same_v<ValueType, std::monostate>)
            {
                return JsonObject{std::monostate{}};
            }
            else if constexpr (std::is_same_v<ValueType, JSONList>)
            {
                for (const auto& it : innerValue)
                {
                    if (std::holds_alternative<JsonDict>(it.inner))
                    {
                        const auto& dict = std::get<JsonDict>(it.inner);
                        auto found = dict.find(strKey);
                        if (found != dict.end())
                        {
                            return found->second;
                        }
                    }
                }
                return JsonObject{std::monostate{}};
            }
            else if constexpr (std::is_same_v<ValueType, JsonDict>)
            {
                auto it = innerValue.find(strKey);
                if (it != innerValue.end()) { return it->second; }
                else { return JsonObject{std::monostate{}}; }
            }
            else { return JsonObject{innerValue}; }
        };
        return std::visit(dovisit, inner);
    }

    std::pair<JsonObject, size_t> JsonParser::innerParseMain(std::string_view jsonString)
    {
        // Logger::log(logger::v1::LOG_LEVELS::DEBUG, "Starting parse");
        if (jsonString.empty()) // input string like "".
        {
            return {JsonObject{std::monostate{}}, 0}; // null
        }
        // 这一段写的妙
        if (size_t offset = count_whitespace_begin(jsonString); offset != 0 && offset != std::string_view::npos)
        {
            auto [obj, eaten] = innerParseMain(jsonString.substr(offset));
            // start|{\n    "str"...                not in this function.
            //      |\n    "str"...                 offset = 5   eaten = 0   eaten+offset = 5;                     " is not of them.
            //      |"str"                          offset = 0   eaten = 0   eaten+offset = 0;
            //
            return {{std::move(obj)}, eaten + offset};
        }
        // input string like 114514, 1919.810, 19.8e10
        // 以json[0]作为判定标准      0-9, ., e, -/+
        if (!jsonString.empty() && ('0' <= jsonString[0] && jsonString[0] <= '9' || jsonString[0] == '-' ||
            jsonString[0] == '+'))
        {
            std::regex number_regex("[+-]?[0-9]+(\\.[0-9]*)?([eE][+-]?[0-9]+)?");
            std::cmatch match;
            if (std::regex_search(jsonString.data(), jsonString.data() + jsonString.size(), match, number_regex))
            {
                std::string matched_str = match.str();
                // Logger::log(logger::LOG_LEVELS::DEBUG, "matched_str: " + matched_str);
                // 这里在if中的初始化属于c++17的新标准.
                if (auto num_optional = try_parse_number<int>(matched_str); num_optional.has_value())
                {
                    // 如果进入的是int类型, 比如30, 直接返回了;
                    // 但是如果进入的是30.33, 这里也会执行try_parse_number<int>(matched_str), 但是返回的optional是空,
                    // 不会进入if内部, 向下进入下一个判定double的try_parse_number.
                    return {JsonObject{num_optional.value()}, matched_str.size()};
                }
                // optional可以隐式转换成bool, has_value()函数可以省去. 更进一步, num_optinoal也可以删除.
                if (auto num_optional = try_parse_number<double>(matched_str))
                {
                    return {JsonObject{*num_optional}, matched_str.size()};
                    // * 和 value()区别: value()会检查optional, *运算符不会.
                    // 我们这里已经确定了num_optional进入了if, 一定是有值的, 用*也没问题, 效率更高.
                }
                return {JsonObject{std::monostate{}}, 0}; // 默认返回null
            }
        }
        // true|false|null
        else if ('t' == jsonString[0] || 'f' == jsonString[0] || 'n' == jsonString[0])
        {
            if (jsonString.starts_with("true") && (jsonString.size() == 4 || !std::isalnum(jsonString[4])))
            {
                return {JsonObject{true}, 4};
            }
            else if (jsonString.starts_with("false") && (jsonString.size() == 5 || !std::isalnum(jsonString[5])))
            {
                return {JsonObject{false}, 5};
            }
            else if (jsonString.starts_with("null") && (jsonString.size() == 4 || !std::isalnum(jsonString[4])))
            {
                return {JsonObject{std::monostate{}}, 4};
            }
            // 这里应该不需要考虑吃空格的.
        }
        else if ('"' == jsonString[0])
        {
            const auto& [target, eaten] = innerParseString(jsonString, jsonString[0]);
            return {JsonObject{target}, eaten};
        }
        else if ('\'' == jsonString[0])
        {
            // like " ones process. It can zip on a single function, but ... i don't wanna do.
            const auto& [target, eaten] = innerParseString(jsonString, jsonString[0]);
            return {JsonObject{target}, eaten};
        }

        // 判定数组 [] [1] [1,2,3]
        else if ('[' == jsonString[0])
        {
            std::vector<JsonObject> result;
            size_t i;
            for (i = 1; i < jsonString.size();)
            {
                // []
                if (']' == jsonString[i])
                {
                    ++i;
                    break;
                }
                // [?        ? != ]             jsonString = ?.....]
                auto [obj, eaten] = innerParseMain(jsonString.substr(i));
                /*
                auto temp = parse(jsonString.substr(i));
                auto obj = temp.first;
                auto eaten = temp.second;
                */

                if (0 == eaten)
                {
                    // ERROR eaten return.
                    i = 0;
                    break;
                }
                i += eaten;
                result.push_back(std::move(obj));

                // eaten  [ "key"  : "value" | ]       eaten space in position |
                i += innerParseMain(jsonString.substr(i)).second;

                if (',' == jsonString[i]) { ++i; }
            }
            return {JsonObject{std::move(result)}, i};
        }
        // only support {"": xxx}  (key is string value)        {{"key"}:{xxx}} is invalid
        else if ('{' == jsonString[0])
        {
            std::unordered_map<std::string, JsonObject> result;
            size_t i;
            for (i = 1; i < jsonString.size();)
            {
                if ('}' == jsonString[i])
                {
                    ++i;
                    break;
                }
                // before eaten:           "string":xxx}
                auto [keyobj, keyeaten] = innerParseMain(jsonString.substr(i));
                // in next function stack.
                // after eaten:            :xxx}
                if (0 == keyeaten)
                {
                    // ERROR
                    i = 0;
                    break;
                }
                i += keyeaten;
                // holds_alternative检查变体 v 是否保有可选项 T。     https://zh.cppreference.com/w/cpp/utility/variant/holds_alternative
                if (!std::holds_alternative<std::string>(keyobj.inner)) // is a string objKey;
                {
                    i = 0;
                    break;
                }

                // eaten {"aaa"  | : "aa"}        eaten space in position |
                i += innerParseMain(jsonString.substr(i)).second;

                if (':' == jsonString[i]) { ++i; } // eat :
                // https://zh.cppreference.com/w/cpp/utility/variant/get
                std::string key = std::move(std::get<std::string>(keyobj.inner));

                // now have                xxx}
                auto [valobj, valeaten] = innerParseMain(jsonString.substr(i));
                if (valeaten == 0)
                {
                    // not get obj          ERROR
                    i = 0;
                    break;
                }
                // have get obj;
                i += valeaten;

                //      you can choose one of them to keep/repalce values which have same string key.
                result.insert_or_assign(std::move(key), std::move(valobj));
                // result.try_emplace(std::move(key), std::move(valobj));
                // result.insert_or_assign(std::move(key), std::move(valobj));

                // eaten   { "key"  :  |   "value" }       eaten space in position |
                i += innerParseMain(jsonString.substr(i)).second;

                if (',' == jsonString[i]) { ++i; }
            }
            return {JsonObject{std::move(result)}, i};
        }
        return {JsonObject{std::monostate{}}, 0};
    }

    size_t JsonParser::count_whitespace_begin(const std::string_view jsonString)
    {
        return jsonString.find_first_not_of(" \n\t\r\v\f");
    }

    int JsonParser::hexCharToInt(char ch)
    {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        else if (ch >= 'a' && ch <= 'f')
            return 10 + (ch - 'a');
        else if (ch >= 'A' && ch <= 'F')
            return 10 + (ch - 'A');
        throw std::invalid_argument("Invalid hex character");
    }

    char JsonParser::hexToChar(char high, char low)
    {
        int highValue = hexCharToInt(high);
        int lowValue = hexCharToInt(low);
        return static_cast<char>((highValue << 4) | lowValue);
    }

    std::pair<std::string, size_t> JsonParser::innerParseString(const std::string_view& jsonString, const char quote)
    {
        /*
        size_t nextcomma = jsonString.find('"', 1);
        std::string target{jsonString.substr(1, nextcomma - 1)};
        */
        // 这里最好不要使用find的形式, 因为JSON中有转义字符, 如果输入是"str\"ing"
        // 正确的是 str\"ing      但如果是find, 结果是 str\

        // 我们需要区分模式, 标准库中没有现成的函数供我们使用. 只能自己写一个小算法.

        std::string target;
        char hex1, hex2;

        enum { Raw, Escaped, Hex1, Hex2 } phase = Raw;

        size_t i;
        for (i = 1; i < jsonString.size(); ++i)
        {
            const char& ch = jsonString[i];
            // string_view返回的是常量引用.
            if (phase == Raw)
            {
                if ('\\' == ch) { phase = Escaped; }
                else if ('"' == ch)
                {
                    ++i;
                    break;
                }
                else { target += ch; }
            }
            else if (phase == Escaped)
            {
                // "\x0X666"
                if ('x' == jsonString[i]) { phase = Hex1; }
                else
                {
                    // 如果到了这里, 说明上一个字符是转义字符(而且不是'x'), phase的值变为Escaped
                    target += unescaped_char(ch);
                    phase = Raw;
                }
            }
            else if (phase == Hex1)
            {
                phase = Hex2;
                hex1 = jsonString[i];
            }
            else if (phase == Hex2)
            {
                hex2 = jsonString[i];
                // std::cout << "hex1: " << hex1 << ", hex2: " << hex2 << std::endl;
                // 看到了别人的作业, 感觉自己好笨, 为什么还要多写好多函数, 有现成的charconv不用... 笨笨笨
                // 但是不知道为什么, 我自己的不好用, 编译不了.
                // if (auto num_val = JsonParser::try_parse_number<int>(jsonString.substr(-1, 2), 16))
                // {
                //     target += static_cast<char>(num_val.value());
                // }

                // 这里弃用, 代码烂的不想删除, 裱着用来祭奠
                // 怎样将两个字符转换成一个字
                try { target += hexToChar(hex1, hex2); }
                catch (std::invalid_argument& e)
                {
                    phase = Raw;
                    std::cout << e.what() << std::endl;
                }
                phase = Raw;
            }
        }
        return {std::move(target), i};
    }

    char JsonParser::unescaped_char(const char& ch)
    {
        switch (ch)
        {
        case 'n': return '\n';
        case 'r': return '\r';
        case '0': return '\0';
        case 't': return '\t';
        case 'v': return '\v';
        case 'f': return '\f';
        case 'b': return '\b';
        case 'a': return '\a';
        // no need to add '\',
        default: return ch;
        }
    }

    template <typename T>
    std::optional<T> JsonParser::try_parse_number(const std::string_view str)
    {
        // 这里传入的str可能是int, 也可能是double类型, 但是T类型是不变的, 要么int, 要么double.
        // 所以外层函数必须写两个, 如果T = int不能解析, 马上使用T = double,
        T result;
        // 结构化绑定（C++17 C++20）
        // 利用charconv头文件中的函数, 对内容解析
        const auto& [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result);

        // https://zh.cppreference.com/w/cpp/utility/from_chars
        if (ec == std::errc() && ptr == str.data() + str.size())
        {
            return result;
        }
        return std::nullopt; // optional返回空值
    }

    // Function to print JsonObject
    void JsonParser::printJsonObject(const JsonObject& obj, std::ostream& out, int depth)
    {
        std::string indent(depth, ' ');
        // lambda 的参数使用auto, c++14出现, c++17扩展.
        // 相当于使用template
        auto dovisit = [&](const auto& innerValue)
        {
            using ValueType = std::decay_t<decltype(innerValue)>;
            /* 为什么在这里使用 std::decay_t：
            在 dovisit 函数中使用 std::decay_t 是为了确保 ValueType 是一个更基础的类型，从而使类型匹配更加简单和直观。 具体来说：
            当你传递 innerValue 到 dovisit 中时，decltype(innerValue) 可能会返回一个引用类型或者带有 const / volatile 限定符的类型。
            使用 std::decay_t 可以将这些额外的限定符移除，使得类型匹配更简单。
            例如，将const int& 转换为 int，将 const std::string& 转换为 std::string 。
            */
            if constexpr (std::is_same_v<decltype (innerValue), std::monostate>) { out << indent << "null"; }
            else if constexpr (std::is_same_v<ValueType, bool>) { out << indent << (innerValue ? "true" : "false"); }
            else if constexpr (std::is_same_v<ValueType, int>) { out << indent << innerValue; }
            else if constexpr (std::is_same_v<ValueType, double>) { out << indent << innerValue; }
            else if constexpr (std::is_same_v<ValueType, std::string>) { out << indent << '"' << innerValue << '"'; }
            else if constexpr (std::is_same_v<ValueType, std::vector<JsonObject>>)
            {
                out << indent << "[\n";
                for (size_t i = 0; i < innerValue.size(); ++i)
                {
                    printJsonObject(innerValue[i], out, depth + 2);
                    if (i < innerValue.size() - 1) out << ",";
                    out << "\n";
                }
                out << indent << "]";
            }
            else if constexpr (std::is_same_v<ValueType, std::unordered_map<std::string, JsonObject>>)
            {
                out << indent << "{\n";
                size_t count = 0;
                for (const auto& [key, value] : innerValue)
                {
                    out << indent << "  \"" << key << "\": ";
                    printJsonObject(value, out, depth + 2);
                    if (count < innerValue.size() - 1) out << ",";
                    out << "\n";
                    ++count;
                }
                out << indent << "}";
            }
        };
        std::visit(dovisit, obj.inner);
    }
} // namespace aquawius::json
